/*
 * @Author: 大D
 * @email: 857216393@qq.com
 * @Date: 2023-12-25 14:07:49
 * @Description: welcome页面canvas绘制
 */
// random
function random(min: number, max: number): number {
	return Math.random() * (max - min) + min;
}

// 计算两点之间的距离
function calculateDistance(p1x: number, p1y: number, p2x: number, p2y: number) {
	const xDistance = p1x - p2x,
		yDistance = p1y - p2y;
	return Math.sqrt(Math.pow(xDistance, 2) + Math.pow(yDistance, 2));
}
function createParticles(x: any, y: any) {
	// increase the particle count for a bigger explosion, beware of the canvas performance hit with the increased particles though
	let particleCount = 30;
	while (particleCount--) {
		particles.push(new Particle(x, y));
	}
}
const requestAnimFrame = (function () {
	return (
		window.requestAnimationFrame ||
		function (callback) {
			setTimeout(callback, 1000 / 60);
		}
	);
})();
// 色调
let hue = 120;
// firework collection
const fireworks: any[] = [];
// particle collection
const particles: any[] = [];
// when launching fireworks with a click, too many get launched at once without a limiter, one launch per 5 loop ticks
const limiterTotal = 5;
let limiterTick = 0;
// this will time the auto launches of fireworks, one launch per 80 loop ticks
const timerTotal = 80;
let timerTick = 0;
let mousedown = false;
// mouse x coordinate,
let mx: number = 0;
// mouse y coordinate
let my: number = 0;
// 更新获取鼠标位置数据
export function getMouseData(x: number, y: number, down: boolean) {
	mousedown = down;
	mx = x;
	my = y;
}

// 烟花
class Firework {
	x: number;
	y: number;
	sx: number;
	sy: number;
	tx: number;
	ty: number;
	distanceToTarget: number;
	distanceTraveled: number;
	coordinateCount: number;
	coordinates: any[];
	angle: number;
	speed: number;
	acceleration: number;
	brightness: number;
	targetRadius: number;

	constructor(sx: number, sy: number, tx: number, ty: number) {
		// 实际坐标
		this.x = sx;
		this.y = sy;
		// 开始坐标
		this.sx = sx;
		this.sy = sy;
		// 目标坐标
		this.tx = tx;
		this.ty = ty;
		// 从起点到目标的距离
		this.distanceToTarget = calculateDistance(sx, sy, tx, ty);
		// 移动的总距离
		this.distanceTraveled = 0;
		// 跟踪过去的每个烟花的坐标来创建一个轨迹效果，增加坐标计数来创建更突出的轨迹
		this.coordinates = [];
		this.coordinateCount = 3;
		// 用当前坐标填充初始坐标集合
		while (this.coordinateCount--) {
			this.coordinates.push([this.x, this.y]);
		}
		this.angle = Math.atan2(ty - sy, tx - sx);
		this.speed = 2;
		this.acceleration = 1.05;
		this.brightness = random(50, 70);
		// 目标指示半径
		this.targetRadius = 1;
	}
	update(index: number) {
		// remove last item in coordinates array
		this.coordinates.pop();
		// add current coordinates to the start of the array
		this.coordinates.unshift([this.x, this.y]);

		// cycle the circle target indicator radius
		if (this.targetRadius < 8) {
			this.targetRadius += 0.3;
		} else {
			this.targetRadius = 1;
		}

		// speed up the firework
		this.speed *= this.acceleration;

		// get the current velocities based on angle and speed
		const vx = Math.cos(this.angle) * this.speed,
			vy = Math.sin(this.angle) * this.speed;
		// how far will the firework have traveled with velocities applied?
		this.distanceTraveled = calculateDistance(this.sx, this.sy, this.x + vx, this.y + vy);

		// if the distance traveled, including velocities, is greater than the initial distance to the target, then the target has been reached
		if (this.distanceTraveled >= this.distanceToTarget) {
			createParticles(this.tx, this.ty);
			// remove the firework, use the index passed into the update function to determine which to remove
			fireworks.splice(index, 1);
		} else {
			// target not reached, keep traveling
			this.x += vx;
			this.y += vy;
		}
	}
	draw(ctx: any) {
		ctx.beginPath();
		// move to the last tracked coordinate in the set, then draw a line to the current x and y
		ctx.moveTo(
			this.coordinates[this.coordinates.length - 1][0],
			this.coordinates[this.coordinates.length - 1][1]
		);
		ctx.lineTo(this.x, this.y);
		ctx.strokeStyle = 'hsl(' + hue + ', 100%, ' + this.brightness + '%)';
		ctx.stroke();

		ctx.beginPath();
		// draw the target for this firework with a pulsing circle
		ctx.arc(this.tx, this.ty, this.targetRadius, 0, Math.PI * 2);
		ctx.stroke();
	}
}
// 点
class Particle {
	x: any;
	y: any;
	coordinates: any[];
	coordinateCount: number;
	angle: number;
	speed: number;
	friction: number;
	gravity: number;
	hue: number;
	brightness: number;
	alpha: number;
	decay: number;
	constructor(x: any, y: any) {
		this.x = x;
		this.y = y;
		// 跟踪每个粒子过去的坐标来创建一个轨迹效果，增加坐标计数来创建更突出的轨迹
		this.coordinates = [];
		this.coordinateCount = 5;
		while (this.coordinateCount--) {
			this.coordinates.push([this.x, this.y]);
		}
		// 在所有可能的方向上设置一个随机的角度，以弧度为单位
		this.angle = random(0, Math.PI * 2);
		this.speed = random(1, 10);
		// 减速
		this.friction = 0.95;
		// 重力会把粒子往下拉
		this.gravity = 1;
		// 将色调设置为整个色调变量的随机数+-20
		this.hue = random(hue - 20, hue + 20);
		this.brightness = random(50, 80);
		this.alpha = 1;
		// 设置粒子淡出的速度
		this.decay = random(0.015, 0.03);
	}
	draw(ctx: any) {
		ctx.beginPath();
		// move to the last tracked coordinates in the set, then draw a line to the current x and y
		ctx.moveTo(
			this.coordinates[this.coordinates.length - 1][0],
			this.coordinates[this.coordinates.length - 1][1]
		);
		ctx.lineTo(this.x, this.y);
		ctx.strokeStyle = 'hsla(' + this.hue + ', 100%, ' + this.brightness + '%, ' + this.alpha + ')';
		ctx.stroke();
	}
	update(index: number) {
		// remove last item in coordinates array
		this.coordinates.pop();
		// add current coordinates to the start of the array
		this.coordinates.unshift([this.x, this.y]);
		// slow down the particle
		this.speed *= this.friction;
		// apply velocity
		this.x += Math.cos(this.angle) * this.speed;
		this.y += Math.sin(this.angle) * this.speed + this.gravity;
		// fade out the particle
		this.alpha -= this.decay;

		// remove the particle once the alpha is low enough, based on the passed in index
		if (this.alpha <= this.decay) {
			particles.splice(index, 1);
		}
	}
}

// main demo loop
export function canvasStart(ctx: any, cw: number, ch: number) {
	// this function will run endlessly with requestAnimationFrame
	requestAnimFrame(canvasStart.bind(null, ctx, cw, ch));

	// increase the hue to get different colored fireworks over time
	hue += 0.5;
	// normally, clearRect() would be used to clear the canvas
	// we want to create a trailing effect though
	// setting the composite operation to destination-out will allow us to clear the canvas at a specific opacity, rather than wiping it entirely
	ctx.globalCompositeOperation = 'destination-out';
	// decrease the alpha property to create more prominent trails
	ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';
	ctx.fillRect(0, 0, cw, ch);
	// change the composite operation back to our main mode
	// lighter creates bright highlight points as the fireworks and particles overlap each other
	ctx.globalCompositeOperation = 'lighter';

	const text = 'D`s Blog';
	ctx.font = '80px sans-serif';
	const textData = ctx.measureText(text);
	ctx.fillStyle =
		'rgba(' +
		parseInt(String(random(0, 255))) +
		',' +
		parseInt(String(random(0, 255))) +
		',' +
		parseInt(String(random(0, 255))) +
		',0.3)';
	ctx.fillText(text, cw / 2 - textData.width / 2, ch / 2 - ch / 10);

	// loop over each firework, draw it, update it
	let fireworksLength = fireworks.length;
	while (fireworksLength--) {
		fireworks[fireworksLength].draw(ctx);
		fireworks[fireworksLength].update(fireworksLength);
	}

	// loop over each particle, draw it, update it
	let i = particles.length;
	while (i--) {
		particles[i].draw(ctx);
		particles[i].update(i);
	}

	// launch fireworks automatically to random coordinates, when the mouse isn't down
	if (timerTick >= timerTotal) {
		if (!mousedown) {
			// start the firework at the bottom middle of the screen, then set the random target coordinates, the random y coordinates will be set within the range of the top half of the screen
			for (let h = 0; h < 50; h++) {
				fireworks.push(new Firework(cw / 2, ch / 2, random(0, cw), random(0, ch)));
			}
			timerTick = 0;
		}
	} else {
		timerTick++;
	}
	// limit the rate at which fireworks get launched when mouse is down
	if (limiterTick >= limiterTotal) {
		if (mousedown) {
			// start the firework at the bottom middle of the screen, then set the current mouse coordinates as the target
			fireworks.push(new Firework(cw / 2, ch / 2, mx, my));
			limiterTick = 0;
		}
	} else {
		limiterTick++;
	}
}
